package beanstao.util.collections;

import java.util.ArrayList;

import beanstao.util.Util;

public class LinkedCharArray
{

	public LinkedCharArray()
	{
		this(256);
	}

	public LinkedCharArray(int size)
	{
		if (size < 0)
		{
			Util.makeThrow("width must >0!");
		}
		this.width = size;
		this.cache = new ArrayList<char[]>();
	}

	public LinkedCharArray(String s)
	{
		this(s.length());
		char[] cs = s.toCharArray();
		this.cache.add(cs);
		this.cursor = cs.length;
	}

	private int offset;
	private int cursor;
	private final int width;
	private final ArrayList<char[]> cache;

	public LinkedCharArray push(int e)
	{
		return this.push((char) e);
	}

	public LinkedCharArray push(char e)
	{
		char[] array;
		int row = this.cursor / this.width;
		int i = this.cursor % this.width;
		if ((this.cache.size() == 0) || ((this.cursor != this.offset) && (i == 0)))
		{
			array = new char[this.width];
			this.cache.add(array);
		}
		else
		{
			array = this.cache.get(row);
		}
		array[i] = e;
		this.cursor++;
		return this;
	}

	public LinkedCharArray push(String s)
	{
		char[] cs = s.toCharArray();
		for (char c : cs)
		{
			this.push(c);
		}
		return this;
	}

	public char popFirst()
	{
		return this.innerGet(this.offset++);
	}

	public String popFirst(int num)
	{
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < num; i++)
		{
			sb.append(this.popFirst());
		}
		return sb.toString();
	}

	public char popLast()
	{
		return this.innerGet(--this.cursor);
	}

	public LinkedCharArray popLast(int num)
	{
		for (int i = 0; i < num; i++)
		{
			this.popLast();
		}
		return this;
	}

	public char first()
	{
		if (this.size() == 0)
		{
			return (char) 0;
		}
		return this.innerGet(this.offset);
	}

	public char last()
	{
		if (this.size() == 0)
		{
			return (char) 0;
		}
		return this.innerGet(this.cursor - 1);
	}

	public LinkedCharArray set(int index, char e)
	{
		this.checkBound(index);
		index += this.offset;
		char[] array = this.cache.get(index / this.width);
		array[index % this.width] = e;
		return this;
	}

	private void checkBound(int index)
	{
		if ((index >= this.size()) || (index < 0))
		{
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + this.size());
		}
	}

	public LinkedCharArray clear()
	{
		this.cache.clear();
		this.cursor = 0;
		this.offset = 0;
		return this;
	}

	private char innerGet(int index)
	{
		char[] array = this.cache.get(index / this.width);
		return array[index % this.width];
	}

	public char get(int index)
	{
		this.checkBound(index);
		return this.innerGet(index + this.offset);
	}

	public boolean isEmpty()
	{
		return 0 == this.cursor - this.offset;
	}

	public int size()
	{
		return this.cursor - this.offset;
	}

	public boolean startsWith(String s)
	{
		if (null == s)
		{
			return false;
		}
		if (s.length() > this.size())
		{
			return false;
		}
		return this.startsWith(s.toCharArray());
	}

	public boolean startsWith(char[] cs)
	{
		if (null == cs)
		{
			return false;
		}
		for (int i = 0; i < cs.length; i++)
		{
			if (cs[i] != this.get(i))
			{
				return false;
			}
		}
		return true;
	}

	public boolean endsWith(String s)
	{
		if (null == s)
		{
			return false;
		}
		if (s.length() > this.size())
		{
			return false;
		}
		return this.endsWith(s.toCharArray());
	}

	public boolean endsWith(char[] cs)
	{
		if (null == cs)
		{
			return false;
		}
		if (this.size() < cs.length)
		{
			return false;
		}
		int of = this.size() - cs.length;
		for (int i = 0; i < cs.length; i++)
		{
			if (cs[i] != this.get(of + i))
			{
				return false;
			}
		}
		return true;
	}

	public int[] toIntArray()
	{
		int[] re = new int[this.size()];
		for (int i = 0; i < re.length; i++)
		{
			re[i] = this.get(i);
		}
		return re;
	}

	public char[] toArray()
	{
		char[] re = new char[this.size()];
		for (int i = 0; i < re.length; i++)
		{
			re[i] = this.get(i);
		}
		return re;
	}

	@Override
	public String toString()
	{
		return new String(this.toArray());
	}

	public String popAll()
	{
		String re = new String(this.toArray());
		this.clear();
		return re;
	}
}
